package cn.schoolwow.workflow.module.definition.action.flow;

import cn.schoolwow.quickflow.domain.FlowContext;
import cn.schoolwow.quickflow.flow.BusinessFlow;
import cn.schoolwow.util.domain.check.jsonarray.CheckJSONArray;
import cn.schoolwow.util.domain.query.jsonarray.QueryJSONArray;
import cn.schoolwow.workflow.domain.deploy.DeployGatewayPositionType;
import cn.schoolwow.workflow.domain.deploy.DeployNodeType;
import cn.schoolwow.workflow.domain.deploy.WorkFlowDeployRequest;
import cn.schoolwow.workflow.domain.task.WorkFlowAssigner;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class CheckDeployWorkArrayFlow implements BusinessFlow {
    @Override
    public void executeBusinessFlow(FlowContext flowContext) throws Exception {
        checkNode(flowContext);
        checkJumpNode(flowContext);
        checkStartAndEndNode(flowContext);
        checkStartNodeJumpSize(flowContext);
        checkExclusiveGateway(flowContext);
    }

    @Override
    public String name() {
        return "部署工作流-检查部署工作流数据完整性";
    }

    private void checkNode(FlowContext flowContext){
        JSONObject workflow = flowContext.checkData("workflow", JSONObject.class);

        JSONArray nodes = workflow.getJSONArray("nodes");
        //节点名称不能重复
        CheckJSONArray.newCheck(nodes)
                .notRepeatCheck(Arrays.asList("name"), "节点名称");
        for(int i=0;i<nodes.size();i++){
            JSONObject node = nodes.getJSONObject(i);
            String nodeName = node.getString("name");

            Integer nodeType = node.getInteger("type");
            Validate.notNull(nodeType, "节点类型不能为空!");
            DeployNodeType deployNodeType = DeployNodeType.getDeployNodeType(nodeType);
            switch (deployNodeType){
                case AuditNode:{
                    Validate.notNull(node.get("assigner"), "审批节点必须设置审批人!节点名称:"+nodeName);
                }break;
                case CarbonCopyNode:{
                    Validate.notNull(node.get("assigner"), "抄送节点必须设置抄送人!节点名称:"+nodeName);
                }break;
            }
            if(node.containsKey("assigner")){
                if(!(node.get("assigner") instanceof JSONArray)){
                    throw new IllegalArgumentException("分配人类型必须为数组!当前类型:"+node.get("assigner").getClass().getName()+",节点名称:"+nodeName);
                }
                JSONArray array = node.getJSONArray("assigner");
                try {
                    List<WorkFlowAssigner> workFlowAssignerList = array.toJavaList(WorkFlowAssigner.class);
                }catch (Exception e){
                    throw new IllegalArgumentException("分配人单个元素类型必须为[WorkFlowAssigner]类型!当前异常原因:"+e.getMessage());
                }
            }
        }
    }

    private void checkJumpNode(FlowContext flowContext){
        JSONObject workflow = (JSONObject) flowContext.checkData("workflow");
        WorkFlowDeployRequest workFlowDeployRequest = flowContext.checkInstanceData(WorkFlowDeployRequest.class);

        Set<String> nodeNameSet = new HashSet<>();
        JSONArray nodes = workflow.getJSONArray("nodes");
        for(int i=0;i<nodes.size();i++){
            JSONObject node = nodes.getJSONObject(i);
            nodeNameSet.add(node.getString("name"));
        }

        JSONArray jumpArray = workflow.getJSONArray("jumpArray");
        for(int i=0;i<jumpArray.size();i++){
            JSONObject jump = jumpArray.getJSONObject(i);
            String current = jump.getString("current");
            if(!nodeNameSet.contains(current)){
                throw new IllegalArgumentException("未定义的当前节点名称!节点名称:"+current+",流程定义名称:"+workFlowDeployRequest.name);
            }
            String next = jump.getString("next");
            if(!nodeNameSet.contains(next)){
                throw new IllegalArgumentException("未定义的下一节点名称!节点名称:"+next+",流程定义名称:"+workFlowDeployRequest.name);
            }
            if(!jump.containsKey("condition")){
                jump.put("condition", 0);
            }else{
                int condition = jump.getIntValue("condition");
                switch (condition){
                    case 0:
                    case 1:break;
                    default:{
                        throw new IllegalArgumentException("不支持的节点跳转类型!允许值:[0:完成任务,1:拒绝任务],当前值:"+condition);
                    }
                }
            }
            String jumpName = jump.getString("jumpName");
            if(StringUtils.isBlank(jumpName)){
                jump.put("jumpName", current+"->"+next);
            }
        }
    }

    private void checkStartAndEndNode(FlowContext flowContext){
        JSONObject workflow = flowContext.checkData("workflow", JSONObject.class);
        WorkFlowDeployRequest workFlowDeployRequest = flowContext.checkInstanceData(WorkFlowDeployRequest.class);

        JSONArray nodes = workflow.getJSONArray("nodes");
        int countOfStart = 0, countOfEnd = 0;
        for(int i=0;i<nodes.size();i++){
            JSONObject node = nodes.getJSONObject(i);

            Integer type = node.getInteger("type");
            Validate.notNull(type, "节点类型不能为空");

            switch (type){
                case 0:{
                    countOfStart++;
                    flowContext.putCurrentFlowData("startNodeName", node.getString("name"));
                }break;
                case 2:{
                    countOfEnd++;
                    flowContext.putCurrentFlowData("endNodeName", node.getString("name"));
                }break;
            }
        }
        if(countOfStart!=1){
            throw new IllegalArgumentException("开始节点只能有一个!实际开始节点个数:"+countOfStart+",流程定义名称:"+workFlowDeployRequest.name);
        }
        if(countOfEnd!=1){
            throw new IllegalArgumentException("结束节点只能有一个!实际结束节点个数:"+countOfEnd+",流程定义名称:"+workFlowDeployRequest.name);
        }
    }

    private void checkStartNodeJumpSize(FlowContext flowContext){
        JSONObject workflow = (JSONObject) flowContext.checkData("workflow");
        String startNodeName = (String) flowContext.checkData("startNodeName");
        WorkFlowDeployRequest workFlowDeployRequest = flowContext.checkInstanceData(WorkFlowDeployRequest.class);

        JSONArray jumpArray = workflow.getJSONArray("jumpArray");
        int startNodeJumpSize = 0;
        for(int i=0;i<jumpArray.size();i++) {
            JSONObject jump = jumpArray.getJSONObject(i);

            if(startNodeName.equalsIgnoreCase(jump.getString("current"))){
                startNodeJumpSize++;
            }
        }
        if(startNodeJumpSize!=1){
            throw new IllegalArgumentException("开始节点只能连接一条线!实际开始节点连接个数:"+startNodeJumpSize+",流程定义名称:"+workFlowDeployRequest.name);
        }
    }

    private void checkExclusiveGateway(FlowContext flowContext){
        JSONObject workflow = flowContext.checkData("workflow", JSONObject.class);
        WorkFlowDeployRequest workFlowDeployRequest = flowContext.checkInstanceData(WorkFlowDeployRequest.class);

        JSONArray nodes = workflow.getJSONArray("nodes");
        JSONArray jumpArray = workflow.getJSONArray("jumpArray");
        for(int i=0;i<nodes.size();i++){
            JSONObject node = nodes.getJSONObject(i);

            String name = node.getString("name");
            DeployNodeType deployNodeType = DeployNodeType.getDeployNodeType(node.getIntValue("type"));
            switch (deployNodeType){
                case ParallelGateway:
                case ExclusiveGateway:{
                    Integer gatewayPosition = node.getInteger("gatewayPosition");
                    if(null==gatewayPosition){
                        throw new IllegalArgumentException("网关类型未指定网关位置!网关名称:"+name);
                    }
                    DeployGatewayPositionType deployGatewayPositionType = DeployGatewayPositionType.getDeployGatewayPositionType(gatewayPosition);

                    List<String> predecessorNameList = QueryJSONArray.newQuery(jumpArray)
                            .addQuery("next", name)
                            .execute()
                            .getSingleFieldValueList("current");
                    List<String> successorNameList = QueryJSONArray.newQuery(jumpArray)
                            .addQuery("current", name)
                            .execute()
                            .getSingleFieldValueList("next");
                    switch (deployGatewayPositionType){
                        case Start:{
                            //网关开始前驱节点为一个，后继节点必须有两个及以上
                            if(predecessorNameList.size()!=1){
                                throw new IllegalArgumentException("["+name+"]的[开始位置]的前驱节点必须有且仅有[1]个节点!当前前驱节点:"+predecessorNameList+",工作流定义名称:"+workFlowDeployRequest.name);
                            }
                            if(successorNameList.size()<2){
                                throw new IllegalArgumentException("["+name+"]的[结束位置]的后继节点必须大于等于[2]个节点!当前后继节点:"+successorNameList+",工作流定义名称:"+workFlowDeployRequest.name);
                            }
                        }break;
                        case End:{
                            //网关结束前驱节点为多个，后继节点只能有一个
                            if(predecessorNameList.size()<2){
                                throw new IllegalArgumentException("["+name+"]的[开始位置]的前驱节点必须大于等于[2]个节点!当前前驱节点:"+predecessorNameList+",工作流定义名称:"+workFlowDeployRequest.name);
                            }
                            if(successorNameList.size()!=1){
                                throw new IllegalArgumentException("["+name+"]的[结束位置]的后继节点必须有且仅有[1]个节点!当前后继节点:"+successorNameList+",工作流定义名称:"+workFlowDeployRequest.name);
                            }
                        }break;
                    }
                }break;
            }
        }
    }
}
